/*****************************************************************************
 *  ENTROPY - emerging network to reduce orwellian potency yield
 *
 *  Copyright (C) 2002 Juergen Buchmueller <pullmoll@stop1984.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software Foundation,
 *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 *	$Id: tools.c,v 1.4 2005/07/12 23:12:29 pullmoll Exp $
 *****************************************************************************/
#include "osd.h"
#include "i18n.h"
#include "tools.h"

static const char _hexdigit_lc[] = "0123456789abcdefx";
static const char _hexdigit_uc[] = "0123456789ABCDEFX";

/**************************************************************
 *
 *	This code has separate copyright notice
 *
 **************************************************************/

/*
 * Copyright Patrick Powell 1995
 * This code is based on code written by Patrick Powell (papowell@astart.com)
 * It may be used for any purpose as long as this notice remains intact
 * on all source code distributions
 */

/**************************************************************
 * Original:
 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
 * A bombproof version of doprnt (dopr) included.
 * Sigh.  This sort of thing is always nasty do deal with.	Note that
 * the version here does not include floating point...
 *
 * snprintf() is used instead of sprintf() as it does limit checks
 * for string length.  This covers a nasty loophole.
 *
 * The other functions are there to prevent NULL pointers from
 * causing nast effects.
 *
 * More Recently:
 *	Brandon Long <blong@fiction.net> 9/15/96 for mutt 0.43
 *	This was ugly.	It is still ugly.  I opted out of floating point
 *	numbers, but the formatter understands just about everything
 *	from the normal C string format, at least as far as I can tell from
 *	the Solaris 2.5 printf(3S) man page.
 *
 *	Brandon Long <blong@fiction.net> 10/22/97 for mutt 0.87.1
 *	  Ok, added some minimal floating point support, which means this
 *	  probably requires libm on most operating systems.  Don't yet
 *	  support the exponent (e,E) and sigfig (g,G).	Also, fmtint()
 *	  was pretty badly broken, it just wasn't being exercised in ways
 *	  which showed it, so that's been fixed.  Also, formated the code
 *	  to mutt conventions, and removed dead code left over from the
 *	  original.  Also, there is now a builtin-test, just compile with:
 *			 gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm
 *	  and run snprintf for results.
 *
 *	Thomas Roessler <roessler@guug.de> 01/27/98 for mutt 0.89i
 *	  The PGP code was using unsigned hexadecimal formats.
 *	  Unfortunately, unsigned formats simply didn't work.
 *
 *	Michael Elkins <me@cs.hmc.edu> 03/05/98 for mutt 0.90.8
 *	  The original code assumed that both snprintf() and vsnprintf() were
 *	  missing.	Some systems only have snprintf() but not vsnprintf(), so
 *	  the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
 *
 *	Andrew Tridgell (tridge@samba.org) Oct 1998
 *	  fixed handling of %.0f
 *	  added test for HAVE_LONG_DOUBLE
 *
 * tridge@samba.org, idra@samba.org, April 2001
 *	  got rid of fcvt code (twas buggy and made testing harder)
 *	  added C99 semantics
 *
 * pullmoll@stop1984.com, Nov 2002
 *    add handling of "#" DP_F_NUM for hex; add non-standard
 *    1000s decimal points for %#d, %#i or %#u formats
 *
 **************************************************************/

#define LDOUBLE long double
#define LLONG	long long

static size_t dopr(char *buffer, size_t maxlen, const char *format,
	va_list args);
static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
	char *value, int flags, int min, int max);
static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
	LLONG value, int base, int min, int max, int flags);
static void fmtfp(char *buffer, size_t *currlen, size_t maxlen,
	LDOUBLE fvalue, int min, int max, int flags);
static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c);

/*
 * dopr(): poor man's version of doprintf
 */

/* format read states */
#define DP_S_DEFAULT 0
#define DP_S_FLAGS	 1
#define DP_S_MIN	 2
#define DP_S_DOT	 3
#define DP_S_MAX	 4
#define DP_S_MOD	 5
#define DP_S_CONV	 6
#define DP_S_DONE	 7

/* format flags - Bits */
#define DP_F_MINUS		(1 << 0)
#define DP_F_PLUS		(1 << 1)
#define DP_F_SPACE		(1 << 2)
#define DP_F_NUM		(1 << 3)
#define DP_F_ZERO		(1 << 4)
#define DP_F_UP 		(1 << 5)
#define DP_F_UNSIGNED	(1 << 6)

/* Conversion Flags */
#define DP_C_SHORT	 1
#define DP_C_LONG	 2
#define DP_C_LDOUBLE 3
#define DP_C_LLONG	 4

#define char_to_int(p) ((p)- '0')
#ifndef MAX
#define MAX(p,q) (((p) >= (q)) ? (p) : (q))
#endif

static size_t dopr(char *buffer, size_t maxlen, const char *format,
	va_list args)
{
	char ch;
	LLONG value;
	LDOUBLE fvalue;
	char *strvalue;
	int min;
	int max;
	int state;
	int flags;
	int cflags;
	size_t currlen;

	state = DP_S_DEFAULT;
	currlen = flags = cflags = min = 0;
	max = -1;
	ch = *format++;

	while (state != DP_S_DONE) {
		if (ch == '\0')
			state = DP_S_DONE;

		switch(state) {
		case DP_S_DEFAULT:
			if (ch == '%')
				state = DP_S_FLAGS;
			else
				dopr_outch (buffer, &currlen, maxlen, ch);
			ch = *format++;
			break;
		case DP_S_FLAGS:
			switch (ch) {
			case '-':
				flags |= DP_F_MINUS;
				ch = *format++;
				break;
			case '+':
				flags |= DP_F_PLUS;
				ch = *format++;
				break;
			case ' ':
				flags |= DP_F_SPACE;
				ch = *format++;
				break;
			case '#':
				flags |= DP_F_NUM;
				ch = *format++;
				break;
			case '0':
				flags |= DP_F_ZERO;
				ch = *format++;
				break;
			default:
				state = DP_S_MIN;
				break;
			}
			break;
		case DP_S_MIN:
			if (isdigit((unsigned char)ch)) {
				min = 10*min + char_to_int (ch);
				ch = *format++;
			} else if (ch == '*') {
				min = va_arg (args, int);
				ch = *format++;
				state = DP_S_DOT;
			} else {
				state = DP_S_DOT;
			}
			break;
		case DP_S_DOT:
			if (ch == '.') {
				state = DP_S_MAX;
				ch = *format++;
			} else {
				state = DP_S_MOD;
			}
			break;
		case DP_S_MAX:
			if (isdigit((unsigned char)ch)) {
				if (max < 0)
					max = 0;
				max = 10*max + char_to_int (ch);
				ch = *format++;
			} else if (ch == '*') {
				max = va_arg (args, int);
				ch = *format++;
				state = DP_S_MOD;
			} else {
				state = DP_S_MOD;
			}
			break;
		case DP_S_MOD:
			switch (ch) {
			case 'h':
				cflags = DP_C_SHORT;
				ch = *format++;
				break;
			case 'l':
				cflags = DP_C_LONG;
				ch = *format++;
				if (ch == 'l') {        /* It's a long long */
					cflags = DP_C_LLONG;
					ch = *format++;
				}
				break;
			case 'L':
				cflags = DP_C_LDOUBLE;
				ch = *format++;
				break;
			default:
				break;
			}
			state = DP_S_CONV;
			break;
		case DP_S_CONV:
			switch (ch) {
			case 'd':
			case 'i':
				if (cflags == DP_C_SHORT)
					value = (LLONG)va_arg (args, int);
				else if (cflags == DP_C_LONG)
					value = (LLONG)va_arg (args, long int);
				else if (cflags == DP_C_LLONG)
					value = (LLONG)va_arg (args, LLONG);
				else
					value = (LLONG)va_arg (args, int);
				fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
				break;
			case 'o':
				flags |= DP_F_UNSIGNED;
				if (cflags == DP_C_SHORT)
					value = (LLONG)va_arg (args, unsigned int);
				else if (cflags == DP_C_LONG)
					value = (LLONG)va_arg (args, unsigned long int);
				else if (cflags == DP_C_LLONG)
					value = (LLONG)va_arg (args, unsigned LLONG);
				else
					value = (LLONG)va_arg (args, unsigned int);
				fmtint (buffer, &currlen, maxlen, value, 8, min, max, flags);
				break;
			case 'u':
				flags |= DP_F_UNSIGNED;
				if (cflags == DP_C_SHORT)
					value = (LLONG)va_arg (args, unsigned int);
				else if (cflags == DP_C_LONG)
					value = (LLONG)va_arg (args, unsigned long int);
				else if (cflags == DP_C_LLONG)
					value = (LLONG)va_arg (args, unsigned LLONG);
				else
					value = (LLONG)va_arg (args, unsigned int);
				fmtint (buffer, &currlen, maxlen, value, 10, min, max, flags);
				break;
			case 'X':
				flags |= DP_F_UP;
			case 'x':
				flags |= DP_F_UNSIGNED;
				if (cflags == DP_C_SHORT)
					value = (LLONG)va_arg (args, unsigned int);
				else if (cflags == DP_C_LONG)
					value = (LLONG)va_arg (args, unsigned long int);
				else if (cflags == DP_C_LLONG)
					value = (LLONG)va_arg (args, unsigned LLONG);
				else
					value = (LLONG)va_arg (args, unsigned int);
				fmtint (buffer, &currlen, maxlen, value, 16, min, max, flags);
				break;
			case 'f':
				if (cflags == DP_C_LDOUBLE)
					fvalue = (LDOUBLE)va_arg (args, LDOUBLE);
				else
					fvalue = (LDOUBLE)va_arg (args, double);
				/* um, floating point? */
				fmtfp (buffer, &currlen, maxlen, fvalue, min, max, flags);
				break;
			case 'E':
				flags |= DP_F_UP;
			case 'e':
				if (cflags == DP_C_LDOUBLE)
					fvalue = (LDOUBLE)va_arg (args, LDOUBLE);
				else
					fvalue = (LDOUBLE)va_arg (args, double);
				break;
			case 'G':
				flags |= DP_F_UP;
			case 'g':
				if (cflags == DP_C_LDOUBLE)
					fvalue = (LDOUBLE)va_arg (args, LDOUBLE);
				else
					fvalue = (LDOUBLE)va_arg (args, double);
				break;
			case 'c':
				dopr_outch (buffer, &currlen, maxlen, (char)va_arg (args, int));
				break;
			case 's':
				strvalue = va_arg (args, char *);
				if (max == -1) {
					if (strvalue == NULL)
						max = strlen("<NULL>");
					else
						max = strlen(strvalue);
				}
				if (min > 0 && max >= 0 && min > max) max = min;
				fmtstr (buffer, &currlen, maxlen, strvalue, flags, min, max);
				break;
			case 'p':
				strvalue = (char *)va_arg (args, void *);
				fmtint (buffer, &currlen, maxlen, (long) strvalue, 16, min, max, flags);
				break;
			case 'n':
				if (cflags == DP_C_SHORT) {
					short int *num;
					num = va_arg (args, short int *);
					*num = (short)currlen;
				} else if (cflags == DP_C_LONG) {
					long int *num;
					num = va_arg (args, long int *);
					*num = (long int)currlen;
				} else if (cflags == DP_C_LLONG) {
					LLONG *num;
					num = va_arg (args, LLONG *);
					*num = (LLONG)currlen;
				} else {
					int *num;
					num = va_arg (args, int *);
					*num = currlen;
				}
				break;
			case '%':
				dopr_outch (buffer, &currlen, maxlen, ch);
				break;
			case 'w':
				/* not supported yet, treat as next char */
				ch = *format++;
				break;
			default:
				/* Unknown, skip */
				break;
			}
			ch = *format++;
			state = DP_S_DEFAULT;
			flags = cflags = min = 0;
			max = -1;
			break;
		case DP_S_DONE:
			break;
		default:
			/* hmm? */
			break; /* some picky compilers need this */
		}
	}
	if (maxlen != 0) {
		if (currlen < maxlen - 1)
			buffer[currlen] = '\0';
		else if (maxlen > 0)
			buffer[maxlen - 1] = '\0';
	}

	return currlen;
}

static void fmtstr(char *buffer, size_t *currlen, size_t maxlen,
	char *value, int flags, int min, int max)
{
	int padlen, strln;	   /* amount to pad */
	int cnt = 0;

#ifdef DEBUG_SNPRINTF
	printf("fmtstr min=%d max=%d s=[%s]\n", min, max, value);
#endif
	if (value == NULL) {
		value = "<NULL>";
	}

	for (strln = 0; value[strln]; ++strln); /* strlen */
	padlen = min - strln;
	if (padlen < 0)
		padlen = 0;
	if (flags & DP_F_MINUS)
		padlen = -padlen; /* Left Justify */

	while ((padlen > 0) && (cnt < max)) {
		dopr_outch (buffer, currlen, maxlen, ' ');
		--padlen;
		++cnt;
	}
	while (*value && (cnt < max)) {
		dopr_outch (buffer, currlen, maxlen, *value++);
		++cnt;
	}
	while ((padlen < 0) && (cnt < max)) {
		dopr_outch (buffer, currlen, maxlen, ' ');
		++padlen;
		++cnt;
	}
}

static void fmtint(char *buffer, size_t *currlen, size_t maxlen,
	LLONG value, int base, int min, int max, int flags)
{
	int signvalue = 0;
	unsigned LLONG uvalue;
	char convert[32];
	const char *_hexdigit;
	int place = 0;
	int spadlen = 0; /* amount to space pad */
	int zpadlen = 0; /* amount to zero pad */
	int add = 0;

	if (max < 0)
		max = 0;

	uvalue = (unsigned LLONG)value;

	if(!(flags & DP_F_UNSIGNED)) {
		if (value < 0) {
			signvalue = '-';
			uvalue = (unsigned LLONG)-value;
		} else {
			if (flags & DP_F_PLUS)	/* Do a sign (+/i) */
				signvalue = '+';
			else if (flags & DP_F_SPACE)
				signvalue = ' ';
		}
	}

	if (flags & DP_F_UP)	/* Should characters be upper case? */
		_hexdigit = _hexdigit_uc;
	else
		_hexdigit = _hexdigit_lc;

	do {
		convert[place++] = _hexdigit[uvalue % (unsigned)base];
		uvalue = (uvalue / (unsigned)base);
		/* for DP_F_NUM in base 10 add apostrophes every 1000s */
		if (flags & DP_F_NUM) {
			if (base == 10 && uvalue > 0 && 3 == (place%4)) {
				convert[place++] = 39;
			}
		}
	} while (uvalue > 0 && place < 32);

	if (32 == place)
		place--;
	convert[place] = '\0';

	if (flags & DP_F_NUM) {
		switch (base) {
		case 10:
			add = place / 4;
			break;
		case 16:
			add = 2;
			break;
		}
	}
	zpadlen = max + add - place;
	spadlen = min + add - MAX (max, place) - (signvalue ? 1 : 0);
	if (zpadlen < 0)
		zpadlen = 0;
	if (spadlen < 0)
		spadlen = 0;
	if (flags & DP_F_ZERO) {
		zpadlen = MAX(zpadlen, spadlen);
		spadlen = 0;
	}
	if (flags & DP_F_MINUS)
		spadlen = -spadlen; /* Left Justifty */

#ifdef DEBUG_SNPRINTF
	printf("zpad: %d, spad: %d, min: %d, max: %d, place: %d\n",
		   zpadlen, spadlen, min, max, place);
#endif

	/* Spaces */
	while (spadlen > add) {
		dopr_outch (buffer, currlen, maxlen, ' ');
		--spadlen;
	}

	/* Sign */
	if (signvalue)
		dopr_outch (buffer, currlen, maxlen, (char)signvalue);


	/* Leading 0x */
	if (flags & DP_F_NUM) {
		if (base == 16) {
			dopr_outch (buffer, currlen, maxlen, '0');
			dopr_outch (buffer, currlen, maxlen, _hexdigit[16]);
		}
	}

	/* Zeros */
	while (zpadlen > 0) {
		dopr_outch (buffer, currlen, maxlen, '0');
		--zpadlen;
	}

	/* Digits */
	while (place > 0)
		dopr_outch (buffer, currlen, maxlen, convert[--place]);

	/* Left Justified spaces */
	add = -add;
	while (spadlen < add) {
		dopr_outch (buffer, currlen, maxlen, ' ');
		++spadlen;
	}
}

static LDOUBLE POW10(int exp)
{
	LDOUBLE result = 1;

	while (exp > 0) {
		result *= 10;
		exp--;
	}

	return result;
}

static LLONG ROUND(LDOUBLE value)
{
	LLONG intpart;

	intpart = (LLONG)value;
	value = value - intpart;
	if (value >= 0.5)
		intpart++;

	return intpart;
}

/* A replacement for modf that doesn't need the math library.
 * Should be portable, but slow
 */
static double my_modf(double x0, double *iptr)
{
	int i;
	long l = 0;
	double x = x0;
	double f = 1.0;

	for (i = 0; i < 100; i++) {
		l = (long)x;
		if (l <= (x+1) && l >= (x-1))
			break;
		x *= 0.1;
		f *= 10.0;
	}

	if (i == 100) {
		/* yikes! the number is beyond what we can handle. What do we do? */
		(*iptr) = 0;
		return 0;
	}

	if (i != 0) {
		double i2;
		double ret;

		ret = my_modf(x0-l*f, &i2);
		(*iptr) = l*f + i2;
		return ret;
	}

	(*iptr) = l;
	return x - (*iptr);
}


static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
	LDOUBLE fvalue, int min, int max, int flags)
{
	int signvalue = 0;
	LDOUBLE ufvalue;
	char iconvert[311];
	char fconvert[311];
	const char *_hexdigit = _hexdigit_lc;
	int iplace = 0;
	int fplace = 0;
	int padlen = 0; /* amount to pad */
	int zpadlen = 0;
	int index;
	double intpart;
	double fracpart;
	double temp;

	/*
	 * AIX manpage says the default is 0, but Solaris says the default
	 * is 6, and sprintf on AIX defaults to 6
	 */
	if (max < 0)
		max = 6;

	ufvalue = fvalue;
	if (ufvalue < 0)
		ufvalue = -ufvalue;

	if (fvalue < 0) {
		signvalue = '-';
	} else {
		if (flags & DP_F_PLUS) { /* Do a sign (+/i) */
			signvalue = '+';
		} else {
			if (flags & DP_F_SPACE)
				signvalue = ' ';
		}
	}

	/*
	 * Sorry, we only support 16 digits past the decimal because of our
	 * conversion method
	 */
	if (max > 16)
		max = 16;

	/* We "cheat" by converting the fractional part to integer by
	 * multiplying by a factor of 10
	 */

	temp = ufvalue;
	my_modf(temp, &intpart);

	fracpart = ROUND((POW10(max)) * (ufvalue - intpart));

	if (fracpart >= POW10(max)) {
		intpart++;
		fracpart -= POW10(max);
	}

	/* Convert integer part */
	do {
		temp = intpart;
		my_modf(intpart * 0.1, &intpart);
		temp = temp * 0.1;
		index = (int) ((temp - intpart + 0.05) * 10.0);
		/* printf ("%llf, %f, %x\n", temp, intpart, index); */
		iconvert[iplace++] = _hexdigit[index];
	} while (intpart > 0 && iplace < 311);

	if (311 == iplace)
		iplace--;
	iconvert[iplace] = '\0';

	/* Convert fractional part */
	if (fracpart > 0) {
		do {
			temp = fracpart;
			my_modf(fracpart * 0.1, &fracpart);
			temp = temp * 0.1;
			index = (int) ((temp - fracpart + 0.05) * 10.0);
			/* printf ("%lf, %lf, %ld\n", temp, fracpart, index); */
			fconvert[fplace++] = _hexdigit[index];
		} while (fracpart > 0 && fplace < 311);
		if (fplace == 311)
			fplace--;
	}
	fconvert[fplace] = '\0';

	/* -1 for decimal point, another -1 if we are printing a sign */
	padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0);
	zpadlen = max - fplace;
	if (zpadlen < 0)
		zpadlen = 0;
	if (padlen < 0)
		padlen = 0;
	if (flags & DP_F_MINUS)
		padlen = -padlen; /* Left Justifty */

	if ((flags & DP_F_ZERO) && padlen > 0) {
		if (signvalue) {
			dopr_outch (buffer, currlen, maxlen, (char)signvalue);
			--padlen;
			signvalue = 0;
		}
		while (padlen > 0) {
			dopr_outch (buffer, currlen, maxlen, '0');
			--padlen;
		}
	}
	while (padlen > 0) {
		dopr_outch (buffer, currlen, maxlen, ' ');
		--padlen;
	}
	if (signvalue)
		dopr_outch (buffer, currlen, maxlen, (char)signvalue);

	while (iplace > 0)
		dopr_outch (buffer, currlen, maxlen, iconvert[--iplace]);

#ifdef DEBUG_SNPRINTF
	printf("fmtfp: fplace=%d zpadlen=%d\n", fplace, zpadlen);
#endif

	/*
	 * Decimal point.  This should probably use locale to find the correct
	 * char to print out.
	 */
	if (max > 0) {
		dopr_outch (buffer, currlen, maxlen, '.');

		while (fplace > 0)
			dopr_outch (buffer, currlen, maxlen, fconvert[--fplace]);
	}

	while (zpadlen > 0) {
		dopr_outch (buffer, currlen, maxlen, '0');
		--zpadlen;
	}

	while (padlen < 0) {
		dopr_outch (buffer, currlen, maxlen, ' ');
		++padlen;
	}
}

static void dopr_outch(char *buffer, size_t *currlen, size_t maxlen, char c)
{
	if (*currlen < maxlen) {
		buffer[(*currlen)] = c;
	}
	(*currlen)++;
}

int pm_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
{
	return dopr(str, count, fmt, args);
}

int pm_snprintf(char *str, size_t count, const char *fmt, ...)
{
	int ret;
	va_list ap;

	va_start(ap, fmt);
	ret = pm_vsnprintf(str, count, fmt, ap);
	va_end(ap);

	return ret;
}

int pm_spprintf(char *dst, char *base, size_t size, const char *fmt, ...)
{
	size_t currlen = (size_t)(dst - base);
	int ret;
	va_list ap;

	if ((int)currlen < 0) {
		return 0;
	}
	if (currlen >= size - 1) {
		base[size - 1] = '\0';
		return 0;
	}
	va_start(ap, fmt);
	ret = pm_vsnprintf(dst, size - currlen, fmt, ap);
	va_end(ap);

	return ret;
}

int pm_vasprintf(char **ptr, const char *fmt, va_list ap)
{
	int ret;

	*ptr = NULL;

	ret = pm_vsnprintf(NULL, 0, fmt, ap);
	if (ret <= 0)
		return ret;

	*ptr = (char *)xcalloc(ret + 1, sizeof(char));
	ret = pm_vsnprintf(*ptr, ret + 1, fmt, ap);

	return ret;
}

int pm_append(char **pdst, char **pbase, size_t *psize, const char *fmt, ...)
{
	va_list ap;
	char *dst = *pdst;
	char *base = *pbase;
	size_t size = *psize;
	size_t offs = (size_t)(dst - base);
	int ret;

	va_start(ap, fmt);
	ret = pm_vsnprintf(NULL, 0, fmt, ap);
	if (ret <= 0)
		return ret;
	/* see if we must expand *base */
	if ((size_t)(offs + ret + 1) >= size) {
		while (size < (size_t)(offs + ret + 1))
			size <<= 1;
		base = (char *)xrealloc(base, size);
		dst = base + offs;
		*psize = size;
		*pbase = base;
	}
	ret = pm_vsnprintf(dst, size - offs, fmt, ap);
	va_end(ap);
	*pdst = dst + ret;
	return ret;
}

int pm_asprintf(char **ptr, const char *fmt, ...)
{
	va_list ap;
	int ret;

	va_start(ap, fmt);
	ret = pm_vasprintf(ptr, fmt, ap);
	va_end(ap);

	return ret;
}

int bitsum(uint32_t n) {
	n = (n & 0x55555555) + ((n >> 1) & 0x55555555);
	n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
	n = (n + (n >> 4)) & 0x0f0f0f0f;
	n = (n + (n >> 8));
	n = (n + (n >> 16));
	return n & 0xff;
}

const char *usec_str(uint64_t us)
{
#undef	BUFNUM
#define	BUFNUM	8
	static char *buff[BUFNUM] = {
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
	};
	static int which = 0;
	uint32_t usec, sec;

	which = (which + 1) % BUFNUM;
	xfree(buff[which]);

	usec = (uint32_t)(us % 1000000);
	sec = (uint32_t)(us / 1000000);

	pm_asprintf(&buff[which], "%u.%06us", sec, usec);

	return buff[which];
}

void tv_add(struct timeval *dst, const struct timeval *src, uint64_t add)
{
	uint32_t usec, sec;

	usec = (uint32_t)(add % 1000000);
	sec = (uint32_t)(add / 1000000);

	dst->tv_usec = src->tv_usec + usec;
	dst->tv_sec = src->tv_sec + sec;
	while (dst->tv_usec >= 1000000) {
		dst->tv_usec -= 1000000;
		dst->tv_sec += 1;
	}
}

void tv_sub(struct timeval *dst, const struct timeval *src, uint64_t sub)
{
	uint32_t usec, sec;

	usec = (uint32_t)(sub % 1000000);
	sec = (uint32_t)(sub / 1000000);

	dst->tv_usec = src->tv_usec - usec;
	dst->tv_sec = src->tv_sec - sec;
	while (dst->tv_usec < 0) {
		dst->tv_usec += 1000000;
		dst->tv_sec -= 1;
	}
}

int64_t tv_diff(const struct timeval *dst, const struct timeval *src)
{
	return (int64_t)1000000 * (dst->tv_sec - src->tv_sec) +
		(dst->tv_usec - src->tv_usec);
}

int tv_cmp(const struct timeval *dst, const struct timeval *src)
{
	if (dst->tv_sec == src->tv_sec) {
		if (dst->tv_usec == src->tv_usec)
			return 0;
		else if (dst->tv_usec > src->tv_usec)
			return +1;
		return -1;
	}
	if (dst->tv_sec > src->tv_sec)
		return 1;
	return -1;
}

const char *datetime_str(time_t t)
{
#undef	BUFNUM
#undef	BUFSIZE
#define	BUFNUM	4
#define	BUFSIZE	32
	static char *buff[BUFNUM] = {NULL, NULL, NULL, NULL};
	static int which = 0;
	struct tm tm;

	which = (which + 1) % BUFNUM;
	xfree(buff[which]);
	memcpy(&tm, gmtime(&t), sizeof(tm));
	pm_asprintf(&buff[which], "%04u-%02u-%02u %02u:%02u:%02u",
		tm.tm_year + 1900,
		tm.tm_mon + 1,
		tm.tm_mday,
		tm.tm_hour,
		tm.tm_min,
		tm.tm_sec);
	return buff[which];
}

int parse_tm_date(struct tm *tm, const char *src)
{
	int mday = 0, mon = 0, year = 0;
	size_t i;

	tm->tm_year = 0;
	tm->tm_mon = 0;
	tm->tm_mday = 0;

	if (NULL == src) {
		/* NULL argument leaves the fields set to zero */
		return 0;
	}

	for (i = 0; src[i]; i++) {
		if (!isdigit(src[i]))
			break;
		year = year * 10;
		year = year + src[i] - '0';
	}
	if (src[i] == '-' || src[i] == '.')
		i++;
	for (/* */; src[i]; i++) {
		if (!isdigit(src[i]))
			break;
		mon = mon * 10;
		mon = mon + src[i] - '0';
	}
	if (src[i] == '-' || src[i] == '.')
		i++;
	for (/* */; src[i]; i++) {
		if (!isdigit(src[i]))
			break;
		mday = mday * 10;
		mday = mday + src[i] - '0';
	}
	if (year >= 1900 && year <= 2099 &&
		mon >= 1 && mon <= 12 &&
		mday >= 1 && mday <= 31) {
		tm->tm_year = year - 1900;
		tm->tm_mon = mon - 1;
		tm->tm_mday = mday;
		return 0;
	}

	errno = EINVAL;
	return -1;
}

int parse_tm_time(struct tm *tm, const char *src)
{
	int hour = 0, min = 0, sec = 0;
	size_t i;

	tm->tm_hour = 0;
	tm->tm_min = 0;
	tm->tm_sec = 0;

	if (NULL == src) {
		/* NULL argument leaves the fields set to zero */
		return 0;
	}

	for (i = 0; src[i]; i++) {
		if (!isdigit(src[i]))
			break;
		hour = hour * 10;
		hour = hour + src[i] - '0';
	}
	if (src[i] == ':' || src[i] == '.' || src[i] == '-')
		i++;
	for (/* */; src[i]; i++) {
		if (!isdigit(src[i]))
			break;
		min = min * 10;
		min = min + src[i] - '0';
	}
	if (src[i] == ':' || src[i] == '.' || src[i] == '-')
		i++;
	for (/* */; src[i]; i++) {
		if (!isdigit(src[i]))
			break;
		sec = sec * 10;
		sec = sec + src[i] - '0';
	}
	if (hour >= 0 && hour <= 23 &&
		min >= 0 && min <= 59 &&
		sec >= 0 && sec <= 59) {
		tm->tm_hour = hour;
		tm->tm_min = min;
		tm->tm_sec = sec;
		return 0;
	}

	errno = EINVAL;
	return -1;
}

const char *age_str(const char *LANG, const char *CHARSET, time_t t0, time_t t)
{
#undef	BUFNUM
#define	BUFNUM	4
	static char *buff[BUFNUM] = {NULL, NULL, NULL, NULL};
	static int which = 0;
	unsigned diff = t > t0 ? t - t0 : t0 - t;
	unsigned w, d, h, m, s;

	which = (which + 1) % BUFNUM;
	xfree(buff[which]);

	s = diff % 60;
	m = (diff / 60) % 60;
	h = (diff / 60 / 60) % 24;
	d = (diff / 60 / 60 / 24) % 7;
	w = diff / 60 / 60 / 24 / 7;
	if (diff < 60) {
		pm_asprintf(&buff[which], "%u%s",
			s, s == 1 ? LO("sec") : LO("secs"));
	} else if (diff < 60 * 60) {
		pm_asprintf(&buff[which], "%u%s %u%s",
			m, LO("mins"),
			s, s == 1 ? LO("sec") : LO("secs"));
	} else if (diff < 24 * 60 * 60) {
		pm_asprintf(&buff[which], "%u%s %u%s",
			h, h == 1 ? LO("hour") : LO("hours"),
			m, m == 1 ? LO("min") : LO("mins"));
	} else if (diff < 7 * 24 * 60 * 60) {
		if (m == 0) {
			pm_asprintf(&buff[which], "%u%s %u%s",
				d, d == 1 ? LO("day") : LO("days"),
				h, h == 1 ? LO("hour") : LO("hours"));
		} else {
			pm_asprintf(&buff[which], "%u%s %u,%u%s",
				d, d == 1 ? LO("day") : LO("days"),
				h, m * 10 / 60, LO("hours"));
		}
	} else {
		if (h == 0) {
			pm_asprintf(&buff[which], "%u%s %u%s",
				w, w == 1 ? LO("week") : LO("weeks"),
				d, d == 1 ? LO("day") : LO("days"));
		} else {
			pm_asprintf(&buff[which], "%u%s %u,%u%s",
				w, w == 1 ? LO("week") : LO("weeks"),
				d, h * 10 / 24, LO("days"));
		}
	}
	return buff[which];
}

const char *flags_str(int flags)
{
#undef	BUFNUM
#undef	BUFSIZE
#define	BUFNUM	4
#define	BUFSIZE	(12+1)
	static char *buff[BUFNUM] = {NULL, NULL, NULL, NULL};
	static int which = 0;
	char *dst;

	which = (which + 1) % BUFNUM;
	xfree(buff[which]);
	dst = buff[which] = xcalloc(BUFSIZE, sizeof(char));
	dst[ 0] = (flags & (1 <<  0)) ? '0' : '-';
	dst[ 1] = (flags & (1 <<  1)) ? '1' : '-';
	dst[ 2] = (flags & (1 <<  2)) ? '2' : '-';
	dst[ 3] = (flags & (1 <<  3)) ? '3' : '-';
	dst[ 4] = (flags & (1 <<  4)) ? '4' : '-';
	dst[ 5] = (flags & (1 <<  5)) ? '5' : '-';
	dst[ 6] = (flags & (1 <<  6)) ? '6' : '-';
	dst[ 7] = (flags & (1 <<  7)) ? '7' : '-';
	dst[ 8] = (flags & (1 <<  8)) ? '8' : '-';
	dst[ 9] = (flags & (1 <<  9)) ? '9' : '-';
	dst[10] = (flags & (1 << 10)) ? 'a' : '-';
	dst[11] = (flags & (1 << 11)) ? 'b' : '-';
	dst[12] = '\0';
	return dst;
}

#define	SIZEk	((uint64_t)1000)
#define	SIZEK	((uint64_t)1024)
#define	SIZEm	((uint64_t)1000*1000)
#define	SIZEM	((uint64_t)1024*1024)
#define	SIZEg	((uint64_t)1000*1000*1000)
#define	SIZEG	((uint64_t)1024*1024*1024)

/*
 *	strtoul_KMG()
 *	convert a number to an unsigned long long (uint64_t).
 *	The number can have a multilplier appended to it:
 *	k = 1000
 *	K = 1024
 *	m = 1000000
 *	M = 1024*1024 = 1048576
 *	g = 1000000000
 *	G = 1024*1024*1024 = 1073741824
 */
uint64_t strtoul_KMG(const char *src)
{
	char *mult;
	uint64_t val;

	val = (uint64_t) strtoul(src, &mult, 0);
	if (NULL != mult) {
		while (*mult == '\t' || *mult == ' ')
			mult++;
		if (*mult == 'K')
			val *= SIZEK;
		else if (*mult == 'k')
			val *= SIZEk;
		else if (*mult == 'M')
			val *= SIZEM;
		else if (*mult == 'm')
			val *= SIZEm;
		else if (*mult == 'G')
			val *= SIZEG;
		else if (*mult == 'g')
			val *= SIZEg;
	}
	return val;
}

const char *size_KMG(uint64_t size)
{
#undef	BUFNUM
#define	BUFNUM	8
	static char *buff[BUFNUM] = {
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
	};
	static int which = 0;

	which = (which + 1) % BUFNUM;
	xfree(buff[which]);

	if (0 == size) {
		pm_asprintf(&buff[which],"0");
	} else if (size < SIZEK) {
		pm_asprintf(&buff[which],"%llu", size);
	} else if (size < SIZEM) {
		if (0 == (size % SIZEK)) {
			pm_asprintf(&buff[which],"%lluK", size / SIZEK);
		} else if (0 == (size % SIZEk)) {
			pm_asprintf(&buff[which],"%lluk", size / SIZEk);
		} else {
			pm_asprintf(&buff[which],"%llu", size);
		}
	} else if (size < SIZEG) {
		if (0 == (size % SIZEM)) {
			pm_asprintf(&buff[which],"%lluM", size / SIZEM);
		} else if (0 == (size % SIZEm)) {
			pm_asprintf(&buff[which],"%llum", size / SIZEm);
		} else if (0 == (size % SIZEK)) {
			pm_asprintf(&buff[which],"%lluK", size / SIZEK);
		} else if (0 == (size % SIZEk)) {
			pm_asprintf(&buff[which],"%lluk", size / SIZEk);
		} else {
			pm_asprintf(&buff[which],"%llu", size);
		}
	} else {
		if (0 == (size % SIZEG)) {
			pm_asprintf(&buff[which],"%lluG", size / SIZEG);
		} else if (0 == (size % SIZEg)) {
			pm_asprintf(&buff[which],"%llug", size / SIZEg);
		} else if (0 == (size % SIZEM)) {
			pm_asprintf(&buff[which],"%lluM", size / SIZEM);
		} else if (0 == (size % SIZEm)) {
			pm_asprintf(&buff[which],"%llum", size / SIZEm);
		} else if (0 == (size % SIZEK)) {
			pm_asprintf(&buff[which],"%lluK", size / SIZEK);
		} else if (0 == (size % SIZEk)) {
			pm_asprintf(&buff[which],"%lluk", size / SIZEk);
		} else {
			pm_asprintf(&buff[which],"%llu", size);
		}
	}
	return buff[which];
}

const char *round_KMG(uint64_t size)
{
#undef	BUFNUM
#define	BUFNUM	8
	static char *buff[BUFNUM] = {
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
	};
	static int which = 0;

	which = (which + 1) % BUFNUM;
	xfree(buff[which]);

	if (0 == size) {
		pm_asprintf(&buff[which],"0");
	} else if (size < SIZEK) {
		pm_asprintf(&buff[which],"%llu", size);
	} else if (size < SIZEM) {
		if (0 == (size % SIZEK) && size < 100 * SIZEK) {
			pm_asprintf(&buff[which],"%lluK", size / SIZEK);
		} else if (0 == (size % SIZEk) && size < 100 * SIZEK) {
			pm_asprintf(&buff[which],"%lluk", size / SIZEk);
		} else {
			pm_asprintf(&buff[which],"%llu.%lluK",
				size/SIZEK, (size%SIZEK)*10/SIZEK);
		}
	} else if (size < SIZEG) {
		if (0 == (size % SIZEM) && size < 100 * SIZEM) {
			pm_asprintf(&buff[which],"%lluM", size / SIZEM);
		} else if (0 == (size % SIZEm) && size < 100 * SIZEM) {
			pm_asprintf(&buff[which],"%llum", size / SIZEm);
		} else {
			pm_asprintf(&buff[which],"%llu.%lluM",
				size/SIZEM, (size%SIZEM)*10/SIZEM);
		}
	} else {
		if (0 == (size % SIZEG) && size < 100 * SIZEG) {
			pm_asprintf(&buff[which],"%lluG", size / SIZEG);
		} else if (0 == (size % SIZEg) && size < 100 * SIZEg) {
			pm_asprintf(&buff[which],"%llug", size / SIZEg);
		} else {
			pm_asprintf(&buff[which],"%llu.%lluG",
				size/SIZEG, (size%SIZEG)*10/SIZEG);
		}
	}
	return buff[which];
}

/* hexstr() - return a byte sequence as string of hex digits */
const char *hexstr(const uint8_t *src, size_t len)
{
#undef	BUFCOUNT
#define	BUFCOUNT	32
	static char *buff[BUFCOUNT] = {
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
	};
	static int which = 0;
	size_t i, size = len*2 + 1;
	char *dst;
	FUN("hexstr");

	which = (which + 1) % BUFCOUNT;
	xfree(buff[which]);
	dst = buff[which] = xcalloc(size, sizeof(char));
	for (i = 0; i < len; i++) {
		*dst++ = _hexdigit_lc[src[i] / 16];
		*dst++ = _hexdigit_lc[src[i] % 16];
	}

	return buff[which];
}

const char *hexdump(const uint8_t *src, size_t len)
{
#undef	BUFCOUNT
#define	BUFCOUNT	8
	static char *buff[BUFCOUNT] = {
		NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL
	};
	static int which = 0;
	size_t size;
	char *dst;
	size_t i, j;

	which = (which + 1) % BUFCOUNT;
	xfree(buff[which]);
	size = ((len / 16) + 1) * 80;
	dst = buff[which] = xcalloc(size, sizeof(char));

	for (i = 0; i < len; i++) {
		if (0 == (i % 16)) {
			*dst++ = _hexdigit_lc[(i >> 12) % 16];
			*dst++ = _hexdigit_lc[(i >>  8) % 16];
			*dst++ = _hexdigit_lc[(i >>  4) % 16];
			*dst++ = _hexdigit_lc[(i >>  0) % 16];
			*dst++ = ':';
		}
		*dst++ = _hexdigit_lc[src[i] / 16];
		*dst++ = _hexdigit_lc[src[i] % 16];
		*dst++ = ' ';
		if (15 == (i % 16)) {
			*dst++ = ' ';
			*dst++ = '-';
			*dst++ = ' ';
			for (j = i - (i % 16); j <= i; j++) {
				*dst++ = (src[j] < 32 || src[j] > 126) ? '.' : src[j];
			}
			*dst++ = '\n';
		}
	}
	if (0 != (i % 16)) {
		for (j = i % 16; j < 16; j++) {
			*dst++ = ' ';
			*dst++ = ' ';
			*dst++ = ' ';
		}
		*dst++ = ' ';
		*dst++ = '-';
		*dst++ = ' ';
		for (j = i - (i % 16); j < i; j++) {
			*dst++ = (src[j] < 32 || src[j] > 126) ? '.' : src[j];
		}
		*dst++ = '\n';
	}
	*dst = '\0';
	return buff[which];
}

static uint8_t gzmagic[] = {0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00};
#define	GZSRCSIZE	4
#define	GZMAGSIZE	8
#define	GZCRCSIZE	4
#define	GZDSTSIZE	4
#define	GZBUFOFFS (GZSRCSIZE+GZMAGSIZE)

/*
 * gzip()
 * compress a block of data from 'src' of length 'srcsize' into a newly
 * allocated buffer at '*dst' and return the compressed size in '*dstsize'
 * The buffer contains a 4 byte size (compressed) first followed by the
 * standard gzip magic header; appended to the buffer are the CRC32 of
 * the uncompressed data and the uncompressed size as two 4 byte words.
 */
int gzip(void **dst, size_t *dstsize, const void *src, size_t srcsize)
{
	uint8_t *gzbuff;
	uint32_t crc;
	uLong gzsize;
	int rc;
	FUN("gzip");

	*dst = NULL;
	*dstsize = 0;

	gzsize = srcsize * 1001 / 1000 + 12 +
		GZSRCSIZE + GZMAGSIZE + GZCRCSIZE + GZDSTSIZE;
	gzbuff = xmalloc(gzsize);

	crc = crc32(0,NULL,0);
	crc = crc32(crc, (uint8_t *)src, srcsize);
	rc = compress2(&gzbuff[GZBUFOFFS], &gzsize, (Bytef *)src, srcsize, 4);
	if (Z_OK != rc) {
		LOG(L_ERROR,("compress2(...,%#x,%#x,%d) call failed (%d)\n",
			(unsigned)gzsize, (unsigned)srcsize, 4, rc));
		xfree(gzbuff);
		return -1;
	}

	if (gzsize < srcsize) {
		/* strip off the trailing 4 bytes for gzip compatibility */
		gzsize -= GZDSTSIZE;

		/* fill in the .gz header */
		memcpy(&gzbuff[GZSRCSIZE], gzmagic, GZMAGSIZE);

		/* header length in network byte order, magic plus image size */
		gzsize = GZBUFOFFS + gzsize;
		gzbuff[gzsize++] = (uint8_t)(crc);
		gzbuff[gzsize++] = (uint8_t)(crc >> 8);
		gzbuff[gzsize++] = (uint8_t)(crc >> 16);
		gzbuff[gzsize++] = (uint8_t)(crc >> 24);
		gzbuff[gzsize++] = (uint8_t)(srcsize);
		gzbuff[gzsize++] = (uint8_t)(srcsize >> 8);
		gzbuff[gzsize++] = (uint8_t)(srcsize >> 16);
		gzbuff[gzsize++] = (uint8_t)(srcsize >> 24);

		/* finally prepend the gzsize in lsb first byte order */
		gzbuff[0] = (uint8_t)(gzsize);
		gzbuff[1] = (uint8_t)(gzsize >>  8);
		gzbuff[2] = (uint8_t)(gzsize >> 16);
		gzbuff[3] = (uint8_t)(gzsize >> 24);

		*dst = gzbuff;
		*dstsize = gzsize;
	} else {
		/* header length in network byte order, image size */
		gzsize = GZSRCSIZE + srcsize;
		memcpy(&gzbuff[GZSRCSIZE], src, srcsize);
		gzbuff[gzsize++] = (uint8_t)(srcsize);
		gzbuff[gzsize++] = (uint8_t)(srcsize >> 8);
		gzbuff[gzsize++] = (uint8_t)(srcsize >> 16);
		gzbuff[gzsize++] = (uint8_t)(srcsize >> 24);

		/* finally prepend the srcsize in lsb first byte order */
		gzbuff[0] = (uint8_t)(gzsize);
		gzbuff[1] = (uint8_t)(gzsize >>  8);
		gzbuff[2] = (uint8_t)(gzsize >> 16);
		gzbuff[3] = (uint8_t)(gzsize >> 24);
		*dst = gzbuff;
		*dstsize = gzsize;
	}

	return rc;
}

/*
 * gunzip()
 * Uncompress a buffer at 'src' of length 'srcsize' into a newly allocated
 * buffer at '*dst'. If the buffer at 'src' does not contain the gzip magic
 * header, assume it is an umcompressed block and just strip off the
 * 4 bytes in front, returning the raw data in '*dst'.
 */
int gunzip(void **dst, size_t *dstsize, const void *src, size_t srcsize)
{
	const uint8_t *pb = (const uint8_t *)src;
	size_t size;
	uint8_t *buff;
	unsigned int compressed_size;
	uLong uncompressed_size;
	int rc;
	FUN("gunzip");

	*dst = NULL;
	*dstsize = 0;

	if (srcsize < GZSRCSIZE) {
		LOG(L_DEBUG,("corrupt buffer; srcsize %#x <= 4!\n",
			(unsigned)srcsize));
		*dstsize = 1;
		errno = EINVAL;
		return -1;
	}

	size = (pb[0] << 0) | (pb[1] << 8) | (pb[2] << 16) | (pb[3] << 24);

	if (size <= GZDSTSIZE) {
		LOG(L_DEBUG,("corrupt buffer; header size %#x <= 4!\n",
			(unsigned)size));
		*dstsize = 2;
		errno = EINVAL;
		return -1;
	}

	if (size >= 16 * 1024 * 1024) {
		LOG(L_DEBUG,("corrupt buffer; header size %#x >= 16MB!\n",
			(unsigned)size));
		*dstsize = 3;
		errno = EINVAL;
		return -1;
	}

	if (size > GZSRCSIZE+GZMAGSIZE+GZDSTSIZE &&
		0 == memcmp(&pb[GZSRCSIZE], gzmagic, GZMAGSIZE)) {

		uncompressed_size =
			(pb[size-4] <<  0) |
			(pb[size-3] <<  8) |
			(pb[size-2] << 16) |
			(pb[size-1] << 24);

		/* some sanity check: we don't have chunks of more than 16MB */
		if (uncompressed_size > 16 * 1024 * 1024) {
			LOG(L_ERROR,("corrupt buffer; uncompressed_size %#x > 16MB!\n",
				(unsigned)uncompressed_size));
			*dstsize = 4;
			errno = EINVAL;
			return -1;
		}

		/* size of the gz image part */
		compressed_size = size - GZBUFOFFS;

		LOG(L_MINOR,("gzip size:%#x compressed:%#x uncompressed:%#x\n",
			(unsigned)size,
			(unsigned)compressed_size,
			(unsigned)uncompressed_size));

		buff = xcalloc(uncompressed_size + 1, sizeof(char));

		size = uncompressed_size;
		rc = uncompress(buff, &uncompressed_size,
			&pb[GZBUFOFFS], compressed_size);

		if (Z_OK != rc) {
			LOG(L_ERROR,("uncompress(...,%p,%#x) call failed with rc:%d\n",
				&pb[GZBUFOFFS], (unsigned)compressed_size, rc));
			*dstsize = 5;
			errno = EINVAL;
			return -1;
		}

		if (size != uncompressed_size) {
			LOG(L_ERROR,("uncompressed size %#x doesn't match header %#x\n",
				(unsigned)size, (unsigned)uncompressed_size));
			*dstsize = 6;
			errno = EINVAL;
			return -1;
		}

		*dst = buff;
		*dstsize = size;
		return 0;
	}

	size -= 4;
	buff = xcalloc(size + 1, sizeof(char));

	memcpy(buff, &pb[4], size);

	*dst = buff;
	*dstsize = size;

	return 0;
}

#ifdef	__CYGWIN__
/* Windows is braindead.. uh, you knew it? */
int mkstemp_binary(char *path, const char *name)
{
	int fd = -1;
	FUN("mkstemp_binary");

	pm_snprintf(path, MAXPATHLEN, "%s/%s-%s.%d.XXXXXX",
		g_conf->temppath, g_conf->progname, name, getpid());

	fd = mkstemp(path);
	if (-1 == fd) {
		LOG(L_ERROR,("mkstemp('%s') call failed (%s)\n",
			path, strerror(errno)));
		goto bailout;
	}
	close(fd);

	fd = open(path, O_WR, 0644);
	if (-1 == fd) {
		LOG(L_ERROR,("open('%s', O_WR, 0644) call failed (%s)\n",
			path, strerror(errno)));
		goto bailout;
	}

bailout:
	return fd;
}
#else
/* Unix does not distinguish binary from text files. */
int mkstemp_binary(char *path, const char *name)
{
	pm_snprintf(path, MAXPATHLEN, "%s/%s-%s.%d.XXXXXX",
		g_conf->temppath, g_conf->progname, name, getpid());

	return mkstemp(path);
}
#endif

/*
 *	mkstemp_closed()
 *	Use mkstemp to create a filename template and close it.
 *	This will be used for temporary files written to by
 *	functions called from the caller of this.
 */
int mkstemp_closed(char *path, const char *name)
{
	int fd = -1;
	FUN("mkstemp_closed");

	pm_snprintf(path, MAXPATHLEN, "%s/%s-%s.%d.XXXXXX",
		g_conf->temppath, g_conf->progname, name, getpid());

	fd = mkstemp(path);
	if (-1 == fd) {
		LOG(L_ERROR,("msktemp('%s') call failed (%s)\n",
			path, strerror(errno)));
		return -1;
	}
	close(fd);
	
	return 0;
}


int set_signal_handler(int sig, void (*sighandler)(int))
{
	struct sigaction sa;
	int rc;
	FUN("set_signal_handler");

	signal(sig, sighandler);
	if (0 != (rc = sigaction(sig, (struct sigaction *)0, &sa))) {
		LOG(L_ERROR,("sigaction(%d,...) call failed (%s)\n",
			sig, strerror(errno)));
		return rc;
	}
#ifdef SA_RESTART
	sa.sa_flags |= SA_RESTART;
#endif
#ifdef	SA_INTERRUPT
	sa.sa_flags &= ~SA_INTERRUPT;
#endif
	if (0 != (rc = sigaction(sig, &sa, (struct sigaction *)0))) {
		LOG(L_ERROR,("sigaction(%d,...) call failed (%s)\n",
			sig, strerror(errno)));
		return rc;
	}
	return 0;
}
